import { AmbientLight, Box3, Color, DirectionalLight, Fog, LoadingManager, MathUtils, Mesh, MeshPhongMaterial, PCFSoftShadowMap, PerspectiveCamera, PlaneGeometry, RepeatWrapping, Scene, SRGBColorSpace, TextureLoader, WebGLRenderer } from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import URDFLoader from 'urdf-loader';

import Stats from 'three/examples/jsm/libs/stats.module.js';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

let SCREEN_WIDTH = window.innerWidth;
let SCREEN_HEIGHT = window.innerHeight;

let container, stats;

let scene, camera, renderer: WebGLRenderer, robot, controls;

init();
render();

function init()
{
    container = document.createElement('div');
    document.body.appendChild(container);

    // CAMERA

    camera = new PerspectiveCamera();
    camera.position.set(5, 5, 5);
    camera.lookAt(0, 0, 0);

    // SCENE

    scene = new Scene();
    scene.background = new Color(0xffffff);
    scene.fog = new Fog(0xffffff, 1000, 4000);

    scene.add(camera);

    // LIGHTS
    const directionalLight = new DirectionalLight(0xffffff, 1.0);
    directionalLight.castShadow = true;
    directionalLight.shadow.mapSize.setScalar(1024);
    directionalLight.position.set(5, 30, 5);
    scene.add(directionalLight);

    const ambientLight = new AmbientLight(0xffffff, 0.2);
    scene.add(ambientLight);

    //  GROUND

    const gt = new TextureLoader().load('../../../resources/textures/terrain/grasslight-big.jpg');
    const gg = new PlaneGeometry(1600, 1600);
    const gm = new MeshPhongMaterial({ color: 0xffffff, map: gt });

    const ground = new Mesh(gg, gm);
    ground.rotation.x = - Math.PI / 2;
    ground.material.map.repeat.set(640, 640);
    ground.material.map.wrapS = RepeatWrapping;
    ground.material.map.wrapT = RepeatWrapping;
    ground.material.map.colorSpace = SRGBColorSpace;
    // note that because the ground does not cast a shadow, .castShadow is left false
    ground.receiveShadow = true;

    scene.add(ground);

    // RENDERER

    renderer = new WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    // renderer.setAnimationLoop(animate);
    container.appendChild(renderer.domElement);

    //

    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = PCFSoftShadowMap;

    // STATS

    stats = new Stats();
    container.appendChild(stats.dom);

    controls = new OrbitControls(camera, renderer.domElement);
    controls.minDistance = 4;
    controls.target.y = 1;
    controls.update();

    // const arms = "iiwa7";
    // const arms = "iiwa14";
    const arms = "iiwa14/iiwa14_glb";
    // const arms = "rm75/rm75";
    // const arms = "ur5e/ur5e";
    // const arms = "ur10e/ur10e";
    // const arms = "xarm7/xarm7";

    // Load robot
    const manager = new LoadingManager();
    manager.resolveURL = url =>
    {
        return `../../../resources/models/arms/${url}`;
    };
    const loader = new URDFLoader(manager);

    loader.loadMeshCb = function (path, manager, onComplete)
    {
        if (path.endsWith(`.obj`))
        {
            const loader = new OBJLoader(manager);
            loader.load(path, function (object)
            {

                object.position.set(0, 1, 0);
                object.scale.multiplyScalar(1);
                scene.add(object);

                object.traverse(c =>
                {
                    c.castShadow = true;
                    c.receiveShadow = true;
                });

            }, undefined, (err) =>
            {
                onComplete(null, err as any);
            });
        }else  if (path.endsWith(`.glb`))
        {
            const loader = new GLTFLoader(manager);
            loader.load(path, function (gltf)
            {

                onComplete( gltf.scene );

            }, undefined, (err) =>
            {
                onComplete(null, err as any);
            });
        }
        else
        {
            throw `Unknown file format: ${path}`
        }
    };

    loader.load(`${arms}.urdf`, result =>
    {
        robot = result;
    });

    // wait until all the geometry has loaded to add the model to the scene
    manager.onLoad = () =>
    {

        robot.rotation.x = Math.PI / 2;
        robot.traverse(c =>
        {
            c.castShadow = true;
        });
        for (let i = 1; i <= 16; i++)
        {
            if (!robot.joints[`HP${i}`]) continue;

            robot.joints[`HP${i}`].setJointValue(MathUtils.degToRad(30));
            robot.joints[`KP${i}`].setJointValue(MathUtils.degToRad(120));
            robot.joints[`AP${i}`].setJointValue(MathUtils.degToRad(-60));

        }
        robot.updateMatrixWorld(true);

        const bb = new Box3();
        bb.setFromObject(robot);

        robot.position.y -= bb.min.y;
        scene.add(robot);

    };

    onResize();
    window.addEventListener('resize', onResize);

}

function onResize()
{

    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(window.devicePixelRatio);

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

}

function render()
{

    requestAnimationFrame(render);
    renderer.render(scene, camera);

}
